<?php

require_once(BaseConfig::LoadClass('template.class.php'));
require_once(BaseConfig::LoadClass('conecta.class.php' ));

class Filtro {
  /**
   * El arreglo se conforma de la siguiente manera:
   * $__arrTipos["campo"] = "Descripcion del campo";
   */
  var $__arrTipos            ;
  var $__strTabla            ; // Nombre de la tabla
  var $__strIndice           ; // Nombre del campo indice. Este campo ser� el devuelto
  var $__strNombreForma      ; // Identificador de los campos de la forma
  var $__DatosDesdeForma     ; // Cuando una forma ha sido llenada, los datos pasados estar�n aqu�
  var $__strOpcion           ; // Opcion a la que se irá una vez en la busqueda
  var $__strLimitanteBusqueda; // Se limitará la busqueda a estas opciones
  var $__strOrdenBusqueda    ; // Orden de la busqueda. Por default será el indice en forma descendente
  var $__intPaginaDeSesion   ; // Si la información se obtuvo de sesion, aquí se guarda true

  var $__strNombreSesion     ; // Indica que arreglo en la sesión guarda la info de este filtro (Se usará el valor de sesión cuando el filtro para llenar el actual venga vacio, si esta variable es vacia entonces este filtro NO se guardará en sesion)

  var $__arrShortCuts        ; // Arreglo de shortcuts para el buscador, de tipo $arr["indice"]["descripcion"], $arr["indice"]["subquery"]
  
  var $__arrReferencias      ; // Arreglo con las referencias para las busquedas anidadas
  var $__arrValoresReferencia; // Arreglo con los valores de la referencia para busqueda posterior
  
  var $__arrExtraCondiciones ; // Arreglo con condiciones extra que se deben de cumplir para incluirlos en los resultados, En el formato $arr[Llave] = "Valor que debe cumplir esta lave" o $arr[Llave] = array(valores que se deben de cumplir)
  
  var $__strTemplatePrefix   ;
  
  var $__strOrdenExtraPost   ; // Orden extra que se aplicará: viene en POST en "filtro_orden" con el formato "NOMBRECOLUMNA,ASC/DESC"

  /**
   * Constructor para los filtros.
   * Un filtro hace referencia a una (y solo una) tabla.
   * 
   * @param String $strNombreTabla       La tabla de la base de datos que se hará referencia en este filtro.
   * @param String $strNombreIndice      La columna que se utilizará como indice. Debe ser única.
   * @param Array  $arrTipos             Arreglo con los indices que tendrá el filtro. El arreglo se conforma de la siguiente manera: $arrTipos["campo"] = "Descripcion del campo";
   * @param String $strNombreForma       Nombre del FORM en HTML que se generará
   * @param String $strLimitanteBusqueda Parte del query en SQL que limitará los resultados (Parte de un "WHERE")
   * @param String $strOrden             Orden que se dará a los resultados (Parte de un "ORDER BY")
   * @param String $strNombreSesion      Nombre de la variable de sesion a guardar
   * @param Array  $arrReferencias       Para las busquedas especiales (Como las de una tabla) se pasan aqui los datos. Formato similar al de ABC $arrReferencias
   * @param Array  $arrExtraCondiciones  Condiciones extra que se deben de cumplir.
   */
  function CrearFiltro($strNombreTabla, $strNombreIndice, $arrTipos, $strNombreForma, $strLimitanteBusqueda = "", $strOrden = "", $strNombreSesion = "", $arrReferencias = "", $arrExtraCondiciones = "") {
    $this->__arrTipos             = $arrTipos            ;
    $this->__strTabla             = $strNombreTabla      ;
    $this->__strIndice            = $strNombreIndice     ;
    $this->__strNombreForma       = $strNombreForma      ;
    $this->__strOpcion            = ''                   ;
    $this->__strLimitanteBusqueda = $strLimitanteBusqueda;
    $this->__strNombreSesion      = $strNombreSesion     ;
    $this->__intPaginaDeSesion    = 0                    ;
    $this->__arrReferencias       = $arrReferencias      ;
    $this->__arrExtraCondiciones  = $arrExtraCondiciones ;

    if(!$strOrden) {
      $this->__strOrdenBusqueda = "{$this->__strIndice} DESC";
    } else {
      $this->__strOrdenBusqueda = $strOrden;
    }
    
    $this->__strTemplatePrefix = "templates/";
    
    $this->__arrValoresReferencia = array();
    
    if(isset($this->__arrReferencias) && is_array($this->__arrReferencias)) {
      foreach($this->__arrReferencias as $strCampo => $arrInfoCampo) {
        if(isset($arrInfoCampo["tabla"])) {
          if(!isset($arrInfoCampo["tabla" ]["orden"])) $arrInfoCampo["tabla" ]["orden"] = "";
          $this->__arrValoresReferencia[$strCampo] = Variables::GetArrayByTable($arrInfoCampo["tabla" ]["nombre"], $arrInfoCampo["tabla" ]["indice"], $arrInfoCampo["tabla" ]["desc"], $arrInfoCampo["tabla" ]["orden"]);
        } else if(isset($arrInfoCampo["tablam"])) {
          if(!isset($arrInfoCampo["tablam"]["orden"])) $arrInfoCampo["tablam"]["orden"] = "";
          $this->__arrValoresReferencia[$strCampo] = Variables::GetArrayByTable($arrInfoCampo["tablam"]["nombre"], $arrInfoCampo["tablam"]["indice"], $arrInfoCampo["tablam"]["desc"], $arrInfoCampo["tablam"]["orden"]);
        } else if(isset($arrInfoCampo["datos"])) {
          $this->__arrValoresReferencia[$strCampo] = $arrInfoCampo["datos"];
        }
      }
    }
  }

  /**
   * Función que obtiene una descripción de el filtro actual con los parametros pasados.
   *
   * @return string Tabla en HTML con la descripción
   */
  function ObtenerTablaInfoActual() {
    $strRows = '';
    foreach($this->__DatosDesdeForma as $arrInfo) {
      if($arrInfo['visible']) {
        $strRows .= '<tr>';
        $strRows .= '<th>' . $this->__arrTipos[$arrInfo['columna']] . '</th>';
        $strRows .= '<th>';
        switch($arrInfo['op']) {
          case '=' : $strRows .= 'Igual a          '; break;
          case '-' : $strRows .= 'Menor que        '; break;
          case '+' : $strRows .= 'Mayor que        '; break;
          case '-=': $strRows .= 'Menor o igual que'; break;
          case '=+': $strRows .= 'Mayor o igual que'; break;
          case '!=': $strRows .= 'Distinto de      '; break;
          case '%' : $strRows .= 'Que contiene     '; break;
        }
        $strRows .= '</th>';
        if(isset($this->__arrValoresReferencia[$arrInfo['columna']])) {
          $strRows .= '<th>' . $this->__arrValoresReferencia[$arrInfo['columna']][$arrInfo['valor']] . '</th>';
        } else {
          $strRows .= '<th>' . $arrInfo['valor'] . '</th>';
        }
        $strRows .= '</tr>';
      }
    }

    return $strRows;
  }

  /**
   * Función que pone la opción a la que se mandará cuando se ejecute el FORM de este filtro (La parte <input name='option' />)
   *
   * @param String $strOpcion
   */
  function SetOpcionSalida($strOpcion) {
    $this->__strOpcion = $strOpcion;
  }

  /**
   * Función que llena los parametros actuales del filtro (O sea, las cosas que seleccionó el usuario en el form)
   * 
   * @param Array $varPostCopy
   */
  function LlenarDatos($varPostCopy) {
    if(isset($varPostCopy[$this->__strNombreForma])) {
      // Verificar si se cuenta con un orden en el POST
      if(isset($varPostCopy["filtro_orden"])) {
        $this->__strOrdenExtraPost = $varPostCopy["filtro_orden"];
      }
      
      $this->__DatosDesdeForma = $varPostCopy[$this->__strNombreForma];
      if($this->__strNombreSesion) {
        if(!isset($_SESSION["Filtros"])) {
          $_SESSION["Filtros"] = array();
        }
        if(!isset($_SESSION["Filtros"][$this->__strNombreSesion])) {
          $_SESSION["Filtros"][$this->__strNombreSesion] = array();
        }
        $_SESSION["Filtros"][$this->__strNombreSesion]["Filtro"    ] = $this->__DatosDesdeForma;
        $_SESSION["Filtros"][$this->__strNombreSesion]["Pagina"    ] = Variables::GetOption('pagina');
        $_SESSION["Filtros"][$this->__strNombreSesion]["OrdenExtra"] = $this->__strOrdenExtraPost;
        $this->__intPaginaDeSesion = 0;
      }
    } else {
      if($this->__strNombreSesion) {
        if(isset($_SESSION["Filtros"])) {
          if(isset($_SESSION["Filtros"][$this->__strNombreSesion])) {
            if(isset($_SESSION["Filtros"][$this->__strNombreSesion]["Filtro"])) {
              $this->__DatosDesdeForma   = $_SESSION["Filtros"][$this->__strNombreSesion]["Filtro"    ];
              $this->__intPaginaDeSesion = $_SESSION["Filtros"][$this->__strNombreSesion]["Pagina"    ];
              $this->__strOrdenExtraPost = $_SESSION["Filtros"][$this->__strNombreSesion]["OrdenExtra"];
            }
          }
        }
      }
    }
  }

  /**
   * Función que crea parte del select en función de que columna, que operador y que valor son pasados.
   * P.Ej. si se pasan como parametros 'id', '3', '>', se formará una parte de query como:
   * 'id > 3 AND'
   *
   * @param String $strColumna La columna que se está manejando
   * @param String $strValor   El valor con el que se comparará
   * @param String $strOp      La operación con la que se comparará
   * 
   * @return String La parte del query generada.
   */
  function __ConvierteValorPosible($strColumna, $strValor, $strOp) {
    // Primero obtener el valor posible
    $strValorA = '';
    $strPreA   = '';
    $strPosA   = '';
    $strOpA    = '';

    switch($this->__arrTipos[$strColumna]) {
      case 'N':
        $strValorA = $strValor - 0;
        break;
      case 'D':
        $strValorA = date("Y-m-d H:i:s", strtotime($strValor));
        $strPreA   = "'";
        $strPosA   = "'";
        break;
      case 'S':
      default :
        $strValorA = OperacionesDB::CambioValoresNoPosiblesDB($strValor);
        $strPreA   = "'";
        $strPosA   = "'";
        break;
    }

    switch($strOp) {
      case '=':
      case '!=':
        $strOpA = $strOp;
        break;
      case '-':
        $strOpA = '<';
        break;
      case '+':
        $strOpA = '>';
        break;
      case '-=':
        $strOpA = '<=';
        break;
      case '=+':
        $strOpA = '>=';
        break;
      case '%':
        $strOpA  = 'LIKE';
        $strPreA = "'%";
        $strPosA = "%'";
        break;
      default:
        $strOpA = '=';
    }
    
    // Para ver lo de las tablas multiples
    if(is_array($this->__arrReferencias) && isset($this->__arrReferencias[$strColumna]) && isset($this->__arrReferencias[$strColumna]["tablam"])) {
      $strOpA  = 'LIKE';
      $strPreA = "'%\'";
      $strPosA = "\'%'";
    }
    
    if(substr($this->__arrTipos[$strColumna], 0, strlen("Fecha")) == "Fecha") {
      $strColumna = "DATE($strColumna)";
    }

    return "$strColumna $strOpA $strPreA$strValorA$strPosA AND ";
  }

  /**
   * Función que junta los valores del objeto con los valores que se obtuvieron
   * de una forma para crear un query que solo obtenga los rows requeridos por
   * el usuario
   *
   * @return String Consulta a ejecutar en la tabla para obtener los indices
   */
  function __GenerarConsulta($strConsulta = "") {
    $bolWhereYaIncluido = false;
    if($strConsulta == "") {
      $strConsulta = "SELECT {$this->__strIndice} FROM {$this->__strTabla} ";
    } else {
      if(strpos($strConsulta, " WHERE ")) {
        $bolWhereYaIncluido = true;
        if(substr(strtoupper(trim($strConsulta)), -4) == " AND") {
          ; // Ya incluye un AND final
        } else {
          // Necesitamos ponerle un AND final
          $strConsulta .= " AND ";
        }
      }
    }

    $strWhere = $this->__GenerarWhere($bolWhereYaIncluido);
    
    $strConsulta .= $strWhere;
    
    if($this->__strOrdenExtraPost) {
      $arrOrdenExtra = explode(",", $this->__strOrdenExtraPost);
      if($arrOrdenExtra[1] == "ASC") {
        $strConsulta .= " ORDER BY {$arrOrdenExtra[0]} ASC , {$this->__strOrdenBusqueda}";
      } else {
        $strConsulta .= " ORDER BY {$arrOrdenExtra[0]} DESC, {$this->__strOrdenBusqueda}";
      }
    } else {
      $strConsulta .= " ORDER BY {$this->__strOrdenBusqueda}";
    }
    
//    echo $strConsulta . "<br>";

    return $strConsulta;
  }

  function __GenerarWhere($bolWhereYaIncluido = false) {
    $strWhere    = "";

    if(is_array($this->__DatosDesdeForma)) {
      foreach($this->__DatosDesdeForma as $arrDatosColumna) {
        if($arrDatosColumna['visible'] == '1') {
          $strWhere .= $this->__ConvierteValorPosible($arrDatosColumna['columna'], $arrDatosColumna['valor'], $arrDatosColumna['op']);
        }
      }
    }

    if($this->__strLimitanteBusqueda) {
      $strWhere .= $this->__strLimitanteBusqueda . " AND ";
    }
    
    if(is_array($this->__arrExtraCondiciones)) {
      foreach($this->__arrExtraCondiciones as $strIndiceCol => $varValoresCol) {
        if(is_array($varValoresCol)) {
          $strWhere .= "$strIndiceCol IN ('" . join("', ", $varValoresCol) . "') AND ";
        } else {
          $strWhere .= "$strIndiceCol = '" . OperacionesDB::CambioValoresNoPosiblesDB($varValoresCol) . "' AND ";
        }
      }
    }
    
    if($strWhere) {
      if(!$bolWhereYaIncluido) {
        $strWhere = "WHERE " . substr($strWhere, 0, -5);
      } else {
        $strWhere = substr($strWhere, 0, -5);
      }
    }

    return $strWhere;
  }

  /**
   * Función que genera un template en HTML con el filtro. Este template tendrá
   * el form y las opciones que anteriormente se manejaron, por lo que ningun
   * criterio de busqueda anterior se perderá.
   *
   * @param String    $strExtraCodigo            Código extra que llevará el template
   * @param Paginador $oPaginador                Objeto paginador que se utilizará en conjunto con el filtro para hacer paginación de búsqueda.
   * @param boolean   $bolPonerBotonNoPaginacion true si se pondrá un boton que no pagine, y se manden, entonces, todos los resultados en vez de solo los N primeros (N Definido en configuración)
   * @param String    $strOpcionImprimir         Opción a la que se mandará si se oprime el boton de "Imprimir" o el de "Exportar a excel". Si no se pasa estos botones no serán visibles.
   * @param String    $strCallBackJS             Función de javascript que se mandará llamar antes de que se mande la forma a submit
   *
   * @return String Template en HTML con el filtro y las opciones pasadas
   */
  function GeneraTemplateBusqueda($strExtraCodigo = "", $oPaginador = "", $bolPonerBotonNoPaginacion = false, $strOpcionImprimir = "", $strCallBackJS = "") {
    $oTemplate = new Template($this->__strTemplatePrefix);

    $strNombreBusqueda  = $this->__strNombreForma;
    $strOpcion          = $this->__strOpcion;

    $strIndiceBusqueda  = 0;
    $strRows            = "";
    $strShowNoPaginator = "none";
    $intUltimaMostrada  = 0;
    
    if(is_array($this->__DatosDesdeForma)) {
      foreach($this->__DatosDesdeForma as $intIndice => $arrDatos) {
        if(!$arrDatos["visible"]) {
          unset($this->__DatosDesdeForma[$intIndice]);
        }
      }
    }
    
    if(is_array($this->__DatosDesdeForma)) {
      $this->__DatosDesdeForma = array_merge($this->__DatosDesdeForma);
    }
    
    for($strIndiceBusqueda = 0; $strIndiceBusqueda < 10; $strIndiceBusqueda++) {
      
      $strSelected1 = $strSelected2 = $strSelected3 = $strSelected4 = $strSelected5 = $strSelected6 = $strSelected7 = "";
      
      if(isset($this->__DatosDesdeForma[$strIndiceBusqueda]) && ($this->__DatosDesdeForma[$strIndiceBusqueda]["visible"] == "1")) {
        $strColSeleccion   = $this->__DatosDesdeForma[$strIndiceBusqueda]['columna'];
        $strValor          = $this->__DatosDesdeForma[$strIndiceBusqueda]['valor'  ];
        $strVisible        = 1;
        $strMostrarColumna = "";
        $intUltimaMostrada = $strIndiceBusqueda;
        
        switch($this->__DatosDesdeForma[$strIndiceBusqueda]["op"]) {
          case '=' : $strSelected1 = "SELECTED"; break;
          case '-' : $strSelected2 = "SELECTED"; break;
          case '+' : $strSelected3 = "SELECTED"; break;
          case '-=': $strSelected4 = "SELECTED"; break;
          case '=+': $strSelected5 = "SELECTED"; break;
          case '!=': $strSelected6 = "SELECTED"; break;
          case '%' : $strSelected7 = "SELECTED"; break;
        }
      } else {
        $strColSeleccion   = "";
        $strValor          = "";
        $strVisible        = 0;
        $strMostrarColumna = "none";
      }
      
      $strOpsColumnas = "";
      $strColSel = "";
      foreach($this->__arrTipos as $strColumna => $strDescCol) {
        if($strColSel == "") {
          $strColSel = $strColumna;
        }
        
        $strOpsColumnas .= "<option value='$strColumna'";
        if($strColumna == $strColSeleccion) {
          $strOpsColumnas .= " SELECTED";
          $strColSel       = $strColumna;
        }
        $strOpsColumnas .= ">$strDescCol</option>";
      }
      
      if(isset($this->__arrValoresReferencia[$strColSel])) {
        $strSelect = "<option value=''>:: SELECCIONE ::</option>";
        foreach($this->__arrValoresReferencia[$strColSel] as $strColInd => $strValInd) {
          $strColInd = str_replace("'", "", $strColInd);
          $strValInd = str_replace("'", "", $strValInd);
          
          $strSelect .= "<option value='$strColInd'";
          if($strColInd == $strValor) {
            $strSelect .= " SELECTED";
          }
          $strSelect .= ">$strValInd</option>";
        }
        eval('$strRows .= "' . $oTemplate->Get("filtro_row_sel.tpl.php") . '";');
      } else {
        eval('$strRows .= "' . $oTemplate->Get("filtro_row.tpl.php") . '";');
      }
    }
    
    if($oPaginador != "") {
      if($bolPonerBotonNoPaginacion) {
        $strShowNoPaginator = "";
      }
      
      if(!$oPaginador->bolPosiblePaginaAnterior) {
        $strShowButtonAnt = "none";
      } else {
        $strShowButtonAnt = "";
      }
      
      if(!$oPaginador->bolPosiblePaginaSiguiente) {
        $strShowButtonSig = "none";
      } else {
        $strShowButtonSig = "";
      }
      
      $intPagAnt  = $oPaginador->intPaginaActual - 1;
      $intPagSig  = $oPaginador->intPaginaActual + 1;
      $strPagAct  = $oPaginador->intPaginaActual;
      $strTotPags = $oPaginador->intNumPaginas  ;
      
      if($oPaginador->bolMostrarTodos) {
//        $strCodigoPaginador = '';
        $intPagAnt  *= -1;
        $intPagSig  *= -1;
      } else {
//        eval('$strCodigoPaginador = "' . $oTemplate->Get("filtro_paginador.tpl.php") . '";');
      }

      // Si es muy grande, no poner el select
      if($strTotPags > 1000) {
        $strSelectorPagina = $strPagAct;
      } else {
        $strSelectorPagina = "<select id='SelectorPagina$strNombreBusqueda' onchange='CambioSelectPaginador$strNombreBusqueda();'>";
        for($i = 0; $i < $strTotPags; $i++) {
          $strSelectorPagina .= "<option value='" . ($i + 1) . "'";
          if(($i + 1) == $strPagAct) {
            $strSelectorPagina .= " SELECTED";
          }
          $strSelectorPagina .= ">" . ($i + 1) . "</option>";
        }
        $strSelectorPagina .= "</select>";
      }

      eval('$strCodigoPaginador = "' . $oTemplate->Get("filtro_paginador.tpl.php") . '";');
      
      $strRows = "<input type='hidden' name='pagina' value='0' id='pagina$strNombreBusqueda' />$strRows";
    } else {
      $strCodigoPaginador = "";
    }

    if(!$strOpcionImprimir) {
      $strShowImprimir = "none";
    } else {
      $strShowImprimir = "";
    }
    
    $strInfoBusqedaSelectJS = "";
    foreach($this->__arrValoresReferencia as $strColumna => $arrValoresCol) {
      $strColumna = str_replace("'", "", $strColumna);
      $strInfoBusqedaSelectJS .= "  arrInfoBusquedasSel{$strNombreBusqueda}['$strColumna'] = new Array();\n";
      foreach($arrValoresCol as $strIndCol => $strValCol) {
        $strIndCol = str_replace("'", "", $strIndCol);
        $strValCol = str_replace("'", "", $strValCol);
        
        $strInfoBusqedaSelectJS .= "  arrInfoBusquedasSel{$strNombreBusqueda}['$strColumna']['$strIndCol'] = '$strValCol';\n";
      }
      $strInfoBusqedaSelectJS .= "  \n";
    }
    
    $strFiltroOrden = $this->__strOrdenExtraPost;
    
    eval('$strTemplate = "' . $oTemplate->Get("filtro.tpl.php") . '";');
    return $strTemplate;
  }

  /**
   * Función que regresa un arreglo con los indices que cumplen los criterios del filtro.
   *
   * @return Array 
   */
  function ObtenerIndices() {
    $oConecta = new Conecta();
    $oConecta->Consulta($this->__GenerarConsulta());
    
    $arrRet = array();
    while(!$oConecta->EOF) {
      $arrRet[] = $oConecta->Field($this->__strIndice);
      $oConecta->MoveNext();
    }
    
    return $arrRet;
  }


  /**
   * Obtiene el número de filtros que se mandaron con el quero, con los que este objeto trabajará
   *
   * @return int Numero de filtros, 0 si no hay alguno activo
   */
  function ObtenerNumeroFiltros() {
    $intToalFiltros = 0;
    if(is_array($this->__DatosDesdeForma)) {
      foreach($this->__DatosDesdeForma as $intIndice => $arrDatos) {
        if($arrDatos["visible"]) {
          $intToalFiltros++;
        }
      }
    }
    return $intToalFiltros;
  }
}

?>